package com.ys.erp.common.util;


import org.apache.commons.lang3.ObjectUtils;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;

/**
 * @author lwj
 * @date 2022/4/28
 */
public class DateUtil {

    public DateUtil() {
    }

    public static final String DATE_FORMAT_ONE = "yyyy-MM-dd HH:mm:ss";

    public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final String DATE_FORMAT = "yyyy-MM-dd";

    public static final String DATE_FORMAT_TWO = "yyyy-MM-dd";
    public static final String DATE_FORMAT_MONTH_DAY = "MM-dd";

    public static final Integer TIME_UNIT_FLAG = 60;

    public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT_TWO);

    private static final DateTimeFormatter FORMATTER_TWO = DateTimeFormatter.ofPattern(DATE_FORMAT_ONE);

    /**
     * string转LocalDate
     *
     * @param date yyyy-MM-dd格式
     * @return 格式后数据
     */
    public static LocalDate stringToLocalDate(String date) {
        if (ObjectUtils.isEmpty(date)) {
            return null;
        }
        if (date.length() < 10) {
            return null;
        }
        if (date.length() > 10) {
            date = date.substring(0, 10);
        }
        return LocalDate.parse(date, formatter);
    }

    /**
     * 字符串转localDateTime
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDateTime stringToLocalDateTime(String date) {
        return LocalDateTime.parse(date, DateTimeFormatter.ofPattern(DATE_FORMAT_ONE));
    }

    public static Long localDate2SecondTimestamp(LocalDate localDate) {
        return localDateTimeToSecondTimestamp(stringToLocalDateTime(localDateFormat(localDate) + " 00:00:00"));
    }

    /**
     * 字符串转时间戳
     *
     * @param date 日期
     * @return 时间戳
     */
    public static Long stringToTimestamp(String date) {
        if (ObjectUtils.isEmpty(date)) {
            return 0L;
        }
        return localDateTimeToSecondTimestamp(stringToLocalDateTime(date));
    }

    /**
     * 计算两个日期相差的分钟数 LocalDateTime
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 相差的分钟数
     */
    public static Long calcDifferMin(LocalDateTime startTime, LocalDateTime endTime) {
        Long start = localDateTimeToSecondTimestamp(startTime);
        Long end = localDateTimeToSecondTimestamp(endTime);
        return calcDifferMin(start, end);
    }

    /**
     * 计算两个日期相差的天数
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 相差天数
     */
    public static Long calcDifferDay(LocalDateTime startTime, LocalDateTime endTime) {
        LocalDate date1 = startTime.toLocalDate();
        LocalDate date2 = endTime.toLocalDate();
        return Math.abs(date1.toEpochDay() - date2.toEpochDay());
    }

    public static Long calcDifferDay(LocalDate startDate, LocalDate endDate) {
        return Math.abs(endDate.toEpochDay() - startDate.toEpochDay());
    }


    /**
     * 比较两个时间的天数差，返回负天数、零天数或正天数
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 如果开始时间小于结束时间，返回负天数；如果开始时间大于结束时间，返回正天数；如果相等，返回零天数
     */
    public static long compareDays(LocalDateTime startTime, LocalDateTime endTime) {
        return ChronoUnit.DAYS.between(startTime, endTime);
    }

    /**
     * 计算两个日期相差的分钟数     时间戳
     *
     * @param startTime 开始时间戳
     * @param endTime   结束时间戳
     * @return 相差的分钟数
     */
    public static Long calcDifferMin(Long startTime, Long endTime) {
        return (endTime - startTime) / TIME_UNIT_FLAG;
    }

    /**
     * 计算两个日期相差的时间
     *
     * @param startTime 起始时间
     * @param endTime   结束时间
     * @return 相差时间
     */
    public static String calcDifferDate(LocalDateTime startTime, LocalDateTime endTime) {
        Long start = localDateTimeToSecondTimestamp(startTime);
        Long end = localDateTimeToSecondTimestamp(endTime);
        return calcDifferDate(start, end);
    }

    /**
     * 计算两个日期相差的时间
     *
     * @param startTime 起始时间
     * @param endTime   结束时间
     * @return 相差时间字符串
     */
    public static String calcDifferDate(Long startTime, Long endTime) {
        long second = endTime - startTime;
        if (second < TIME_UNIT_FLAG) {
            return second + "秒";
        }
        long min = second / TIME_UNIT_FLAG;
        if (min < TIME_UNIT_FLAG) {
            return min + "分钟";
        }
        long hour = min / TIME_UNIT_FLAG;
        if (hour < TIME_UNIT_FLAG) {
            return hour + "小时";
        }
        return null;
    }

    /**
     * localDateTime转时间戳    秒级别
     *
     * @param localDateTime localDateTime
     * @return 秒级别时间戳
     */
    public static Long localDateTimeToSecondTimestamp(LocalDateTime localDateTime) {
        return localDateTime.toInstant(ZoneOffset.of("+8")).getEpochSecond();
    }

    /**
     * 时间戳转localDateTime
     *
     * @param timestamp 时间戳 秒级别
     * @return localDateTime
     */
    public static LocalDateTime timestampToLocalDateTimeSecond(Long timestamp) {
        return LocalDateTime.ofEpochSecond(timestamp, 0, ZoneOffset.ofHours(8));
    }

    /**
     * 时间戳转localDate
     *
     * @param timestamp 时间戳 秒级别
     * @return localDateTime
     */
    public static LocalDate timestampToLocalDateSecond(Long timestamp) {
        if (ObjectUtils.isEmpty(timestamp)) {
            return null;
        }
        Instant instant = Instant.ofEpochSecond(timestamp);
        return instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();
    }

    /**
     * 格式化localDateTime         yyyy—MM-dd HH:mm:ss
     *
     * @param localDateTime 日期
     * @return 格式化日期
     */
    public static String localDateTimeFormat(LocalDateTime localDateTime) {
        if (ObjectUtils.isEmpty(localDateTime)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMAT_ONE).format(localDateTime);
    }

    /**
     * 格式化localDateTime         yyyy—MM-dd
     *
     * @param localDateTime 日期
     * @return 格式化日期
     */
    public static String localDateTimeFormatTwo(LocalDateTime localDateTime) {
        if (ObjectUtils.isEmpty(localDateTime)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMAT_TWO).format(localDateTime);
    }


    public static String localDateFormatMonthAndDay(LocalDate localDate) {
        if (ObjectUtils.isEmpty(localDate)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMAT_MONTH_DAY).format(localDate);
    }

    public static String localDateTimeFormatMonthAndDay(LocalDateTime localDateTime) {
        if (ObjectUtils.isEmpty(localDateTime)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMAT_MONTH_DAY).format(localDateTime);
    }

    public static String localDateFormat(LocalDate localDate) {
        if (ObjectUtils.isEmpty(localDate)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMAT_TWO).format(localDate);
    }

    /**
     * 判断日期是否在两个日期之间
     *
     * @param startTime  起始时间
     * @param endTime    结束时间
     * @param targetTime 目标时间
     * @return 是否在两个日期间
     */
    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, LocalDateTime targetTime) {
        if (ObjectUtils.isEmpty(startTime) || ObjectUtils.isEmpty(endTime) || ObjectUtils.isEmpty(targetTime)) {
            return false;
        }
        return targetTime.isAfter(startTime) && targetTime.isBefore(endTime);
    }

    public static boolean isSameDay(LocalDateTime time1, LocalDateTime time2) {
        return cn.hutool.core.date.DateUtil.isSameDay(localDateTime2Date(time1), localDateTime2Date(time2));
    }

    public static boolean isSameWeek(LocalDateTime time1, LocalDateTime time2) {
        return cn.hutool.core.date.DateUtil.isSameWeek(localDateTime2Date(time1), localDateTime2Date(time2), true);
    }

    public static boolean isSameMonth(LocalDateTime time1, LocalDateTime time2) {
        return cn.hutool.core.date.DateUtil.isSameMonth(localDateTime2Date(time1), localDateTime2Date(time2));
    }

    public static boolean isSameYear(LocalDateTime time1, LocalDateTime time2) {
        int year = cn.hutool.core.date.DateUtil.year(localDateTime2Date(time1));
        int year1 = cn.hutool.core.date.DateUtil.year(localDateTime2Date(time2));
        return year == year1;
    }

    public static Date localDateTime2Date(LocalDateTime time) {
        ZonedDateTime zonedDateTime = time.atZone(ZoneId.systemDefault());
        // 本地时间线LocalDateTime到即时时间线Instant时间戳
        Instant instant = zonedDateTime.toInstant();
        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
        return Date.from(instant);
    }

    public static int getYear(LocalDateTime time) {
        return cn.hutool.core.date.DateUtil.year(localDateTime2Date(time));
    }

    public static int getMonth(LocalDateTime time) {
        return cn.hutool.core.date.DateUtil.month(localDateTime2Date(time));
    }

    public static int yearDiff(LocalDateTime time, LocalDateTime time2) {
        Period re = Period.between(time.toLocalDate(), time2.toLocalDate());
        return re.getYears();
    }

    public static int yearNowDiff(LocalDateTime time) {
        Period re = Period.between(time.toLocalDate(), LocalDateTime.now().toLocalDate());
        return re.getYears();
    }

    public static int monthDiff(LocalDateTime time, LocalDateTime time2) {
        Period re = Period.between(time.toLocalDate(), time2.toLocalDate());
        return re.getMonths();
    }

    public static int monthNowDiff(LocalDateTime time) {
        Period re = Period.between(time.toLocalDate(), LocalDateTime.now().toLocalDate());
        return re.getMonths();
    }

    /**
     * 获取七天前的时间
     *
     * @return 七天前
     */
    public static LocalDateTime getSevenDayAgo() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DATE, -7);
        return c.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }


    /**
     * 获取两年后的一个时间
     *
     * @return 两年后日期字符串
     */
    public static String getTwoYearsLaterDate() {
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();

        // 计算两年后的日期
        LocalDate twoYearsLater = currentDate.plusYears(2).minusDays(1);

        // 定义日期格式化模式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

        // 返回格式化后的日期字符串
        return twoYearsLater.format(formatter);
    }


    /**
     * 获取两年后的一个时间
     *
     * @return 两年后日期字符串
     */
    public static String dateFormatter(LocalDate date, String pattern) {
        // 定义日期格式化模式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

        // 返回格式化后的日期字符串
        return date.format(formatter);
    }

    /**
     * 获取两年后的一个时间
     *
     * @return 两年后日期字符串
     */
    public static String dateTimeFormatter(LocalDateTime date, String pattern) {
        // 定义日期格式化模式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

        // 返回格式化后的日期字符串
        return date.format(formatter);
    }

    public static LocalDateTime getTimeTwoHoursAgo() {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 减去两个小时
        return now.minusHours(5);
    }

    /**
     * 计算当前时间到目标时间之间相差的分钟数（目标时间为字符串类型）
     *
     * @param targetTimeStr 目标时间字符串（格式："yyyy-MM-dd HH:mm:ss"）
     * @return 剩余分钟数（负数表示已过期）
     */
    public static long getMinutesUntil(String targetTimeStr) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime target = LocalDateTime.parse(targetTimeStr, FORMATTER_TWO);
        return Duration.between(now, target).toMinutes();
    }

    /**
     * 判断目标时间在开始和结束时间范围的位置
     *
     * @param target 目标时间
     * @param start  开始时间
     * @param end    结束时间
     * @return 0: 在 start 之前；1: 区间内（含边界）；2: 在 end 之后
     */
    public static int getTimePosition(LocalDateTime target, LocalDateTime start, LocalDateTime end) {
        if (target.isBefore(start)) {
            return 0;
        } else if (target.isAfter(end)) {
            return 2;
        } else {
            return 1;
        }
    }

}